using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using DataAccessTecnoPez;
using System.Data.OleDb;

namespace BussinessLogicTecnoPez
{
    /// <summary>
    /// Clase de acceso a datos, para objetos genericos, funciona con clases
    /// Con claves simples es decir no combinadas.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class GenericManager<T> where T : new()
    {
        private Exception generatedException;
        /// <summary>
        /// Para los casos en que la excepcion no sea emitida a los metodos llamantes
        /// se utiliza esta propiedad para obtener la excepcion generada en el genericManager.
        /// </summary>
        public Exception GeneratedException
        {
            get
            {
                return generatedException;
            }
        }
        /// <summary>
        /// Enumeracion de los operadores que se usaran en las condiciones
        /// de las consultas a la base de datos, se deberian ir agregando mas
        /// junto con la modificacion correspondiente en los metodo AddFilter
        /// y toStringOperator
        /// </summary>
        public enum filterOperator
        {
            Equals,
            BiggerThan,
            SmallerThan,
            In,
            Like,
            Distinct,
            BiggerEqualsThan,
            SmallerEqualsThan
        }

        Filter sqlFilter = new Filter(); //objeto que tendra el filtro a usar en el metodo GetFilter
        Order sqlOrder = new Order(); //objet que tendra el orden
        string TableName; // nombre de la tabla que se corresponde con el tipo de dato T
        List<Fields> colFields = new List<Fields>(); //listado de campos que tiene el tipo de datos T
        List<FieldsDetails> colFieldsDetail = new List<FieldsDetails>();

        /// <summary>
        /// Contrustor de la clase, dentro del mismo se obtienen el nombre de la tabla
        /// que se corresponde con T, y el listado de campos de T.
        /// </summary>
        public GenericManager()
        {
            #region Obtengo el tableName
            Type t = typeof(T); //obtengo el tipo de T
            Object[] DBt = t.GetCustomAttributes(typeof(DBTable), true);//obtengo los atributos personalizados para el tipo de dato T
            foreach (Object obj in DBt)//por cada atributo
            {
                DBTable t1 = obj as DBTable; //casteo a tipo DBTable
                if (t1 != null) //si no es null es porque el CustomAtributte es de tipo DBTable
                {
                    TableName = t1.ToString(); //lo guardo en la variable
                    break;//termino el bucle
                }
            }
            #endregion

            #region Obtengo los campos
            PropertyInfo[] PI = t.GetProperties();
            foreach (PropertyInfo p in PI)
            {
                Object[] colObj = p.GetCustomAttributes(false);
                foreach (Object obj in colObj)
                {
                    DBField DBf = obj as DBField;
                    if (DBf != null)
                    {
                        Fields item = new Fields(DBf.ToString(), null, p.PropertyType, DBf.IsKey, DBf.IsAutonumeric, DBf.IsForeingKey, DBf.ForeingTable, p);
                        colFields.Add(item);
                    }
                }
            }
            #endregion

            #region Obtengo los campos de detalle
            foreach (PropertyInfo p in PI)
            {
                Object[] colObj = p.GetCustomAttributes(false);
                foreach (Object obj in colObj)
                {
                    DBFieldDetail DBf = obj as DBFieldDetail;
                    if (DBf != null)
                    {
                        FieldsDetails item = new FieldsDetails(DBf.Name, p.PropertyType, DBf.ItemType, p);
                        colFieldsDetail.Add(item);
                    }
                }
            }
            #endregion
        }

        /// <summary>
        /// Debuelve una coleccion de un determinado tipo haciendo
        /// una consulta en la base de datos utilizando el filtro,
        /// al terminar limpia los filtros y las condiciones de ordenamiento.
        /// </summary>
        /// <returns>List</returns>       
        public List<T> GetFilter()
        {
            string masterField = "";
            object masterFieldValue = null;
            StringBuilder QueryStr = new StringBuilder();
            QueryStr.Append("SELECT "); //SELECT 
            bool coma = false;

            foreach (Fields f in colFields)// SELECT Nombre, Apellido
            {
                if (coma)
                {
                    QueryStr.Append(", ");
                }
                else
                {
                    coma = true;
                }
                QueryStr.Append(f.Name);
            }

            QueryStr.Append(" FROM "); // SELECT Nombre, Apellido FROM
            QueryStr.Append(TableName);// SELECT Nombre, Apellido FROM CLIENTES

            #region Carga condicion si existe
            if (sqlFilter.colData.Count > 0 || sqlFilter.strQuery.ToString().Contains("NULL"))
            {
                QueryStr.Append(" WHERE ");
                QueryStr.Append(sqlFilter.strQuery);

            }
            if (sqlOrder.strQuery.ToString() != "")
            {
                QueryStr.Append(" ");
                QueryStr.Append(sqlOrder.strQuery);
            }
            #endregion

            #region Ejecuta la consulta en la base de datos
            OleDbDataReader DR=null;
            using (SQL sql = new SQL())
            {
                try
                {
                    try
                    {
                        sql.SqlString = QueryStr.ToString();

                        if (sqlFilter.colData.Count > 0)
                        {
                            object[] par = new object[sqlFilter.colData.Count];
                            sqlFilter.colData.CopyTo(par);
                            DR = sql.GetDataReader(par);
                        }
                        else
                        {
                            DR = sql.GetDataReader();
                        }
            #endregion

                        #region Devuelve el resultado en una List
                        List<T> Result = new List<T>();

                        if (DR.HasRows)
                        {
                            while (DR.Read())
                            {
                                T obj = new T();
                                foreach (Fields f in colFields)
                                {
                                    if (!(DR[f.Name] is DBNull))
                                    {
                                        if (!f.isForeingKey)
                                        {


                                            f.PropInfo.SetValue(obj, DR[f.Name], null);
                                        }
                                        else
                                        {
                                            Type typeAux = f.fType;
                                            object auxObj = Activator.CreateInstance(typeAux);


                                            f.PropInfo.SetValue(obj, GetOne(auxObj, DR[f.Name], sql), null);

                                        }
                                    }
                                    if (f.isKey)
                                    {
                                        masterField = f.Name;
                                        masterFieldValue = f.PropInfo.GetValue(obj, null);
                                    }
                                }
                                foreach (FieldsDetails fd in colFieldsDetail)
                                {
                                    Type typeAux = fd.fType;
                                    object auxObj2 = Activator.CreateInstance(typeAux);//crea una instancia de un objeto teniendo el tipo
                                    fd.PropInfo.SetValue(obj, DetailGetAll(auxObj2, fd.ItemType, sql, masterField, masterFieldValue), null);
                                }
                                Result.Add(obj);
                            }
                        }
                        ClearFilters();
                        ClearOrders();
                        return Result;
                    }
                    catch
                    {
                        throw;
                    }
                    finally
                    {
                        if (DR != null)
                        {
                            DR.Close();
                            DR.Dispose();
                        }
                    }
                }
                catch
                {
                    throw;
                }
                finally
                {
                    if (DR != null)
                    {
                        DR.Close();
                        DR.Dispose();
                    }
                }
            }

                        #endregion

        }

        public GenericManager<T> getMyInstance()
        {
            return this;
        }

        /// <summary>
        /// Obtiene una instancia de T por medio de la clave primaria, solo funciona para
        /// objetos con 1 sola clave primaria marcada con el correspondiente tag.
        /// </summary>
        /// <param name="primaryIdentityKey"></param>
        /// <returns>Instancia de la clase correspondiente a la clave o null si no exsite la misma</returns>
        public T GetOneByPrimaryKey(object primaryIdentityKey)
        {
            this.ClearFilters();
            foreach (Fields i in colFields)
            {
                if (i.isKey)
                {
                    this.AddFilter(i.PropInfo.Name, filterOperator.Equals, primaryIdentityKey);
                    T result = this.GetOne();
                    this.ClearFilters();
                    return result;
                }
            }
            return default(T);
        }

        /// <summary>
        /// Devuelve un objeto de tipo T, utiliza el filter agregado a traves
        /// de la funcion addFilter. Si mas de uno cumple el filtro, devuelve
        /// el primero. 
        /// </summary>
        /// <returns>
        /// El objeto instanciado, si no esta en la base de datos, devuelve null
        /// </returns>
        public T GetOne()
        {
            string masterField = "";
            object masterFieldValue = null;
            StringBuilder QueryStr = new StringBuilder();
            QueryStr.Append("SELECT "); //SELECT 
            bool coma = false;

            foreach (Fields f in colFields)// SELECT Nombre, Apellido
            {
                if (coma)
                {
                    QueryStr.Append(", ");
                }
                else
                {
                    coma = true;
                }
                QueryStr.Append(f.Name);
            }

            QueryStr.Append(" FROM "); // SELECT Nombre, Apellido FROM
            QueryStr.Append(TableName);// SELECT Nombre, Apellido FROM CLIENTES

            #region Carga condicion si existe
            if (sqlFilter.colData.Count > 0 || sqlFilter.strQuery.ToString().Contains("NULL"))
            {
                QueryStr.Append(" WHERE ");
                QueryStr.Append(sqlFilter.strQuery);

            }
            #endregion

            #region Ejecuta la consulta en la base de datos
            OleDbDataReader DR=null;
            using (SQL sql = new SQL())
            {
                try
                {
                    sql.SqlString = QueryStr.ToString();
                    if (sqlFilter.colData.Count > 0)
                    {
                        object[] par = new object[sqlFilter.colData.Count];
                        sqlFilter.colData.CopyTo(par);
                        DR = sql.GetDataReader(par);
                    }
                    else
                    {
                        DR = sql.GetDataReader();
                    }
            #endregion

                    #region Devuelve el resultado
                    if (DR.HasRows)
                    {
                        DR.Read();
                        T obj = new T();
                        foreach (Fields f in colFields)
                        {
                            if (!(DR[f.Name] is DBNull))
                            {
                                if (!f.isForeingKey)
                                {
                                    f.PropInfo.SetValue(obj, DR[f.Name], null);

                                }
                                else
                                {
                                    Type typeAux = f.fType;
                                    object auxObj = Activator.CreateInstance(typeAux);
                                    f.PropInfo.SetValue(obj, GetOne(auxObj, DR[f.Name], sql), null);
                                }
                                if (f.isKey)
                                {
                                    masterField = f.Name;
                                    masterFieldValue = f.PropInfo.GetValue(obj, null);
                                }
                            }
                        }
                        foreach (FieldsDetails fd in colFieldsDetail)
                        {
                            Type typeAux = fd.fType;
                            object auxObj2 = Activator.CreateInstance(typeAux);//crea una instancia de un objeto teniendo el tipo
                            fd.PropInfo.SetValue(obj, DetailGetAll(auxObj2, fd.ItemType, sql, masterField, masterFieldValue), null);
                        }

                        this.ClearFilters();
                        this.ClearOrders();
                        return obj;
                    }
                }
                catch
                {
                    throw;
                }
                finally
                {
                    if (DR != null)
                    {
                        DR.Close();
                        DR.Dispose();
                    }
                }

            }

            this.ClearFilters();
            this.ClearOrders();
            return default(T);
                    #endregion
        }

        /// <summary>
        /// Esta funcion devuele un objeto del mismo tipo del object que se pasa por 
        /// parametro. Y que tengo como primary Key el pKey que se pasa por parametro.
        /// Por el momento solo funciona si en la tabla tiene un solo PrimaryKey, habria que 
        /// mejorarlo si hace falta.
        /// </summary>
        /// <param name="o"></param>
        /// <param name="pKey"></param>
        /// <returns></returns>
        private object GetOne(object o, object pKey, SQL sql)
        {
            if (pKey.ToString() == "")
            {
                return null;
            }


            string auxTableName = "";
            List<Fields> auxColFields = new List<Fields>();
            List<FieldsDetails> auxColFieldsDetails = new List<FieldsDetails>();
            string masterField = "";
            object masterFieldValue = null;

            Type t = o.GetType();

            #region Obtengo el auxTableName
            Object[] DBt = t.GetCustomAttributes(typeof(DBTable), true);//obtengo los atributos personalizados para el tipo de dato T
            foreach (Object obj in DBt)//por cada atributo
            {
                DBTable t1 = obj as DBTable; //casteo a tipo DBTable
                if (t1 != null) //si no es null es porque el CustomAtributte es de tipo DBTable
                {
                    auxTableName = t1.ToString(); //lo guardo en la variable
                    break;//termino el bucle
                }
            }
            #endregion

            #region Obtengo los campos
            PropertyInfo[] PI = t.GetProperties();
            foreach (PropertyInfo p in PI)
            {
                object[] colObj = p.GetCustomAttributes(false);
                foreach (object obj in colObj)
                {
                    DBField DBf = obj as DBField;
                    if (DBf != null)
                    {
                        Fields item = new Fields(DBf.ToString(), null, p.PropertyType, DBf.IsKey, DBf.IsAutonumeric, DBf.IsForeingKey, DBf.ForeingTable, p);
                        auxColFields.Add(item);
                    }
                }

            }
            #endregion

            #region Obtengo los campos de detalle
            foreach (PropertyInfo p in PI)
            {
                Object[] colObj = p.GetCustomAttributes(false);
                foreach (Object obj in colObj)
                {
                    DBFieldDetail DBf = obj as DBFieldDetail;
                    if (DBf != null)
                    {
                        FieldsDetails item = new FieldsDetails(DBf.Name, p.PropertyType, DBf.ItemType, p);
                        auxColFieldsDetails.Add(item);
                    }
                }
            }
            #endregion

            #region genero el string de la consulta
            StringBuilder QueryStr = new StringBuilder();
            QueryStr.Append("SELECT ");
            bool coma = false;

            foreach (Fields f in auxColFields)
            {
                if (coma)
                {
                    QueryStr.Append(", ");
                }
                else
                {
                    coma = true;
                }
                QueryStr.Append(f.Name);
            }

            QueryStr.Append(" FROM ");
            QueryStr.Append(auxTableName);
            #endregion

            #region Genero la condicion con el Key
            QueryStr.Append(" where ");

            bool pAND = false;
            foreach (Fields f in auxColFields)
            {
                if (f.isKey)
                {
                    if (pAND)
                    {
                        QueryStr.Append(" AND ");
                    }
                    else
                    {
                        pAND = true;
                    }
                    QueryStr.Append(f.Name);
                    QueryStr.Append(" = ?");
                    break;
                }
            }
            #endregion

            #region Ejecuta la consulta en la base del datos

            OleDbDataReader DR=null;
            try
            {
                sql.SqlString = QueryStr.ToString();
                DR = sql.GetDataReader(pKey);
            #endregion

                #region Creo los objetos con los datos traidos por la consulta en la BD
                if (DR.HasRows)
                {
                    DR.Read();
                    foreach (Fields f in auxColFields)
                    {
                        if (!(DR[f.Name] is DBNull))
                        {
                            if (!f.isForeingKey)
                            {
                                f.PropInfo.SetValue(o, DR[f.Name], null);
                            }
                            else
                            {
                                // Si es un foreingKey, creo un objeto para asignarlo como valor
                                //Por ejemplo: el pedido tiene un cliente, genero el cliente
                                //y realizo pedido.Cliente = nuevoCliente.
                                //Este nuevo cliente lo obtengo con el ID que viene en el DataReadre
                                //Usando recursivamente la funcion GetOne
                                Type typeAux = f.fType;
                                object auxObj = Activator.CreateInstance(typeAux);//crea una instancia de un objeto teniendo el tipo
                                f.PropInfo.SetValue(o, GetOne(auxObj, DR[f.Name], sql), null);
                            }
                            if (f.isKey)
                            {
                                masterField = f.Name;
                                masterFieldValue = f.PropInfo.GetValue(o, null);
                            }
                        }
                    }
                    //si es un detalle, obtengo todo el detalle para ese objeto
                    //por ejemplo, traigo todo el detalle del pedido.
                    foreach (FieldsDetails fd in auxColFieldsDetails)
                    {
                        Type typeAux = fd.fType;
                        object auxObj2 = Activator.CreateInstance(typeAux);//crea una instancia de un objeto teniendo el tipo
                        fd.PropInfo.SetValue(o, DetailGetAll(auxObj2, fd.ItemType, sql, masterField, masterFieldValue), null);
                    }

                    //De esta forma no anda, verificar si el cambio a fecto otras cosas.

                    //foreach (FieldsDetails fd in auxColFieldsDetails)
                    //{
                    //    fd.PropInfo.SetValue(o, DetailGetAll(fd.PropInfo.GetValue(o, null), fd.ItemType, sql, masterField, masterFieldValue), null);
                    //}
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                if (DR != null)
                {
                    DR.Close();
                    DR.Dispose();
                }
            }
                #endregion

            return o; //retorno el obejto.
        }

        /// <summary>
        /// Obtiene el detalle para un objeto
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="itemType"></param>
        /// <param name="sql"></param>
        /// <param name="masterField"></param>
        /// <param name="masterFieldValue"></param>
        /// <returns></returns>
        private object DetailGetAll(object obj, Type itemType, SQL sql, string masterField, object masterFieldValue)
        {
            ArrayList DetailsList = new ArrayList();
            Type t = obj.GetType();
            PropertyInfo[] PI = t.GetProperties();
            foreach (PropertyInfo p in PI)
            {
                object[] colObj = p.GetCustomAttributes(false);
                foreach (object o in colObj)
                {
                    DBField DBf = o as DBField;
                    if (DBf != null)
                    {
                        if (DBf.Name == "detail")
                        {
                            //obtengo todos los items
                            DetailsList = ItemDetailGetAll(itemType, sql, masterField, masterFieldValue);
                            //lo seteo en el obeto detalle (es un ArrayList)
                            p.SetValue(obj, DetailsList, null);
                            break;
                        }
                    }
                }
            }
            return obj;
        }

        /// <summary>
        /// Obtiene todos los item de un detalle
        /// </summary>
        /// <param name="itemType"></param>
        /// <param name="sql"></param>
        /// <param name="masterField"></param>
        /// <param name="masterFieldValue"></param>
        /// <returns></returns>
        private ArrayList ItemDetailGetAll(Type itemType, SQL sql, string masterField, object masterFieldValue)
        {
            ArrayList result = new ArrayList();
            string auxTableName = "";
            List<Fields> auxColFields = new List<Fields>();
            List<FieldsDetails> auxColFieldsDetail = new List<FieldsDetails>();

            Type t = itemType;
            #region Obtengo el auxTableName
            Object[] DBt = t.GetCustomAttributes(typeof(DBTable), true);//obtengo los atributos personalizados para el tipo de dato T
            foreach (Object auxObj in DBt)//por cada atributo
            {
                DBTable t1 = auxObj as DBTable; //casteo a tipo DBTable
                if (t1 != null) //si no es null es porque el CustomAtributte es de tipo DBTable
                {
                    auxTableName = t1.ToString(); //lo guardo en la variable
                    break;//termino el bucle
                }
            }
            #endregion

            #region Obtengo los campos
            PropertyInfo[] PI = t.GetProperties();
            foreach (PropertyInfo p in PI)
            {
                Object[] colObj = p.GetCustomAttributes(false);
                foreach (Object auxObj in colObj)
                {
                    DBField DBf = auxObj as DBField;
                    if (DBf != null)
                    {
                        Fields item = new Fields(DBf.ToString(), null, p.PropertyType, DBf.IsKey, DBf.IsAutonumeric, DBf.IsForeingKey, DBf.ForeingTable, p);
                        auxColFields.Add(item);
                    }
                }
            }
            #endregion

            #region Obtengo los campos de detalle
            foreach (PropertyInfo p in PI)
            {
                Object[] colObj = p.GetCustomAttributes(false);
                foreach (Object auxObj in colObj)
                {
                    DBFieldDetail DBf = auxObj as DBFieldDetail;
                    if (DBf != null)
                    {
                        FieldsDetails item = new FieldsDetails(DBf.Name, p.PropertyType, DBf.ItemType, p);
                        auxColFieldsDetail.Add(item);
                    }
                }
            }
            #endregion

            #region genero el string de la consulta
            StringBuilder QueryStr = new StringBuilder();
            QueryStr.Append("SELECT ");
            bool coma = false;

            foreach (Fields f in auxColFields)
            {
                if (coma)
                {
                    QueryStr.Append(", ");
                }
                else
                {
                    coma = true;
                }
                QueryStr.Append(f.Name);
            }

            QueryStr.Append(" FROM ");
            QueryStr.Append(auxTableName);
            #endregion

            #region Genero la condicion con el MasterField
            if (masterField != "" && masterFieldValue != null)
            {
                QueryStr.Append(" WHERE ");//SELECT * FROM DETALLEPEDIDO WHERE
                QueryStr.Append(masterField);//SELECT * FROM DETALLEPEDIDO WHERE idPedido
                QueryStr.Append(" = ");//SELECT * FROM DETALLEPEDIDO WHERE id =
                QueryStr.Append(masterFieldValue); //SELECT * FROM DETALLEPEDIDO WHERE id = 1
            }
            #endregion

            #region Ejecuta la consulta en la base del datos
            OleDbDataReader DR=null;
            try
            {
                sql.SqlString = QueryStr.ToString();
                DR = sql.GetDataReader();
            #endregion

                #region Creo los objetos con los datos traidos por la consulta en la BD
                if (DR.HasRows)
                {
                    while (DR.Read())
                    {
                        object objAux = Activator.CreateInstance(itemType);
                        foreach (Fields f in auxColFields)
                        {
                            if (!(DR[f.Name] is DBNull))
                            {
                                if (!f.isForeingKey)
                                {
                                    f.PropInfo.SetValue(objAux, DR[f.Name], null);
                                }
                                else
                                {
                                    // Si es un foreingKey, creo un objeto para asignarlo como valor
                                    //Por ejemplo: el pedido tiene un cliente, genero el cliente
                                    //y realizo pedido.Cliente = nuevoCliente.
                                    //Este nuevo cliente lo obtengo con el ID que viene en el DataReadre
                                    //Usando recursivamente la funcion GetOne
                                    Type typeAux = f.fType;
                                    object auxObj2 = Activator.CreateInstance(typeAux);//crea una instancia de un objeto teniendo el tipo
                                    f.PropInfo.SetValue(objAux, GetOne(auxObj2, DR[f.Name], sql), null);
                                }
                                if (f.isKey)
                                {
                                    masterField = f.Name;
                                    masterFieldValue = f.PropInfo.GetValue(objAux, null);
                                }
                            }
                        }
                        //si es un detalle, obtengo todo el detalle para ese objeto
                        //por ejemplo, traigo todo el detalle del pedido.
                        foreach (FieldsDetails fd in auxColFieldsDetail)
                        {
                            Type typeAux = fd.ItemType;
                            object auxObj2 = Activator.CreateInstance(typeAux);//crea una instancia de un objeto teniendo el tipo
                            fd.PropInfo.SetValue(objAux, DetailGetAll(auxObj2, fd.ItemType, sql, masterField, masterFieldValue), null);
                        }
                        result.Add(objAux);
                    }
                }
                #endregion

            }
            catch
            {
                throw;
            }
            finally
            {
                if (DR != null)
                {
                    DR.Close();
                    DR.Dispose();
                }
            }

            return result;
        }

        /// <summary>
        /// Debuelve una coleccion de un determinado tipo haciendo
        /// una consulta en la base de datos
        /// </summary>
        /// <returns>List</returns>
        public List<T> GetAll()
        {
            string masterField = "";
            object masterFieldValue = null;
            StringBuilder QueryStr = new StringBuilder();
            QueryStr.Append("SELECT ");
            bool coma = false;

            foreach (Fields f in colFields)
            {
                if (coma)
                {
                    QueryStr.Append(", ");
                }
                else
                {
                    coma = true;
                }
                QueryStr.Append(f.Name);
            }

            QueryStr.Append(" FROM ");
            QueryStr.Append(TableName);

            #region Ejecuta la consulta en la base de datos
            OleDbDataReader DR=null;
            using (SQL sql = new SQL())
            {
                try
                {

                    sql.SqlString = QueryStr.ToString();
                    DR = sql.GetDataReader();
            #endregion

                    #region Devuelve el resultado en una List
                    List<T> Result = new List<T>();

                    if (DR.HasRows)
                    {
                        while (DR.Read())
                        {
                            T obj = new T();
                            foreach (Fields f in colFields)
                            {
                                if (!(DR[f.Name] is DBNull))
                                {
                                    if (!f.isForeingKey)
                                    {

                                        f.PropInfo.SetValue(obj, DR[f.Name], null);

                                    }
                                    else
                                    {
                                        Type typeAux = f.fType;
                                        object auxObj = Activator.CreateInstance(typeAux);

                                        f.PropInfo.SetValue(obj, GetOne(auxObj, DR[f.Name], sql), null);
                                    }
                                }
                                if (f.isKey)
                                {
                                    masterField = f.Name;
                                    masterFieldValue = f.PropInfo.GetValue(obj, null);
                                }
                            }
                            foreach (FieldsDetails fd in colFieldsDetail)
                            {
                                Type typeAux = fd.fType;
                                object auxObj2 = Activator.CreateInstance(typeAux);//crea una instancia de un objeto teniendo el tipo
                                fd.PropInfo.SetValue(obj, DetailGetAll(auxObj2, fd.ItemType, sql, masterField, masterFieldValue), null);
                            }
                            Result.Add(obj);
                        }
                    }
                    this.ClearOrders();
                    this.ClearFilters();
                    return Result;
                }
                catch
                {
                    throw;
                }
                finally
                {
                    if (DR != null)
                    {
                        DR.Close();
                        DR.Dispose();
                    }
                }
            }

                    #endregion
        }

        /// <summary>
        /// realiza un insert de un objeto, junto con su detalle.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public bool Insert(T obj)
        {
            bool insertRes = false;
            StringBuilder QueryStr = new StringBuilder();
            QueryStr.Append("INSERT INTO ");
            QueryStr.Append(TableName);
            QueryStr.Append(" (");
            try
            {
                #region se agrega los campos en string de la consulta
                bool coma = false;
                foreach (Fields f in colFields)
                {

                    if (!f.isAutonumeric)
                    {
                        if (coma)
                        {
                            QueryStr.Append(", ");
                        }
                        else
                        {
                            coma = true;
                        }
                        QueryStr.Append(f.Name);
                    }
                }
                #endregion

                #region carga el string "?" en la consulta
                QueryStr.Append(") VALUES(");
                int cont = 0;
                coma = false;
                foreach (Fields f in colFields)
                {
                    if (!f.isAutonumeric)
                    {
                        if (coma)
                        {
                            QueryStr.Append(", ");
                        }
                        else
                        {
                            coma = true;
                        }
                        QueryStr.Append("?");
                        cont++;
                    }
                }
                QueryStr.Append(")");
                #endregion

                #region carga los valores de los parametros en un array
                object[] colData = new object[cont];
                int lastIndexUsed = 0;
                foreach (Fields f in colFields)
                {
                    if (!f.isAutonumeric)
                    {
                        colData[lastIndexUsed] = getFieldValue(f, obj); //f.PropInfo.GetValue(obj, null);
                        lastIndexUsed++;
                    }
                }
                #endregion

                #region Realiza la inserccion en la base de datos
                using (SQL sql = new SQL())
                {
                    sql.SqlString = QueryStr.ToString();
                    int res = sql.ExecuteNonQuery(colData);
                    insertRes = res > 0 ? true : false;
                #endregion

                    //Agregado by rod para rellenar los campos autonumericos!
                    OleDbDataReader DR = null;
                    try
                    {
                        QueryStr = new StringBuilder();
                        foreach (Fields f in colFields)
                        {
                            if (f.isAutonumeric)
                            {
                                QueryStr.Append(" select max(");
                                QueryStr.Append(f.Name);
                                QueryStr.Append(") from ");
                                QueryStr.Append(TableName);
                                sql.SqlString = QueryStr.ToString();
                                DR = sql.GetDataReader();
                                if (DR.Read())
                                {
                                    f.PropInfo.SetValue(obj, DR[0], null);
                                }
                                DR.Close();

                            }
                        }
                        DR = null;
                        #region Obtengo el id del registro insertado

                        foreach (FieldsDetails fd in colFieldsDetail)
                        {
                            QueryStr = new StringBuilder();
                            QueryStr.Append("SELECT TOP 1 ");
                            QueryStr.Append(fd.Name);
                            QueryStr.Append(" FROM ");
                            QueryStr.Append(TableName);
                            QueryStr.Append(" ORDER BY ");
                            QueryStr.Append(fd.Name);
                            QueryStr.Append(" DESC ");
                            sql.SqlString = QueryStr.ToString();
                            DR = sql.GetDataReader();
                            DR.Read();
                            break;
                        }

                        #endregion

                        #region Realiza la inserccion de los detalles si existen
                        foreach (FieldsDetails fd in colFieldsDetail)
                        {
                            if (fd.PropInfo.GetValue(obj, null) != null)
                            {
                                DetailInsert(fd.PropInfo.GetValue(obj, null), fd.ItemType, sql, fd.Name, DR[fd.Name]);
                            }
                        }
                        if (DR != null)
                        {
                            DR.Close();
                        }
                        #endregion
                    }
                    catch
                    {
                        throw;
                    }
                    finally
                    {
                        if (DR != null)
                        {
                            DR.Close();
                            DR.Dispose();
                        }
                    }

                }
                return insertRes;
            }
            catch (Exception ex)
            {
                generatedException = ex;
                return false;
            }
        }

        /// <summary>
        /// Inserta el detalle, esta funcion se deberia llamar desde la funcion insert.
        /// Se le debe pasar el campo que corresponde con el id de la tabla maestra(ej.: idPedido)
        /// y el valor para ese id (ej.: 1). Esto es para saber a que Pedido se inserta el detalle.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="itemType"></param>
        /// <param name="sql"></param>
        /// <param name="masterField"></param>
        /// <param name="masterFieldValue"></param>
        private void DetailInsert(object obj, Type itemType, SQL sql, string masterField, object masterFieldValue)
        {
            ArrayList DetailsList = new ArrayList();
            Type t = obj.GetType();
            PropertyInfo[] PI = t.GetProperties();
            foreach (PropertyInfo p in PI)
            {
                object[] colObj = p.GetCustomAttributes(false);
                foreach (object o in colObj)
                {
                    DBField DBf = o as DBField;
                    if (DBf != null)
                    {
                        if (DBf.Name == "detail")
                        {
                            DetailsList = (ArrayList)p.GetValue(obj, null);
                            break;
                        }
                    }
                }
            }

            foreach (object i in DetailsList)
            {
                ItemDetailInsert(i, itemType, sql, masterField, masterFieldValue);
            }
        }

        /// <summary>
        /// Realiza un Insert en la BD tomando el objeto, y el tipo de objeto
        /// Esta realizado para insertar los detalles. Se puede mejorar.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="t"></param>
        private void ItemDetailInsert(object obj, Type t, SQL sql, string masterField, object masterFieldValue)
        {
            string insTableName = "";
            List<Fields> insColFields = new List<Fields>();
            List<FieldsDetails> insColFieldsDetail = new List<FieldsDetails>();

            #region Obtengo el tableName
            Object[] DBt = t.GetCustomAttributes(typeof(DBTable), true);//obtengo los atributos personalizados para el tipo de dato T
            foreach (Object auxObj in DBt)//por cada atributo
            {
                DBTable t1 = auxObj as DBTable; //casteo a tipo DBTable
                if (t1 != null) //si no es null es porque el CustomAtributte es de tipo DBTable
                {
                    insTableName = t1.ToString(); //lo guardo en la variable
                    break;//termino el bucle
                }
            }
            #endregion

            #region Obtengo los campos
            PropertyInfo[] PI = t.GetProperties();
            foreach (PropertyInfo p in PI)
            {
                Object[] colObj = p.GetCustomAttributes(false);
                foreach (Object auxObj in colObj)
                {
                    DBField DBf = auxObj as DBField;
                    if (DBf != null)
                    {
                        Fields item = new Fields(DBf.ToString(), null, p.PropertyType, DBf.IsKey, DBf.IsAutonumeric, DBf.IsForeingKey, DBf.ForeingTable, p);
                        insColFields.Add(item);
                    }
                }
            }
            #endregion

            #region Obtengo los campos de detalle
            foreach (PropertyInfo p in PI)
            {
                Object[] colObj = p.GetCustomAttributes(false);
                foreach (Object auxObj in colObj)
                {
                    DBFieldDetail DBf = auxObj as DBFieldDetail;
                    if (DBf != null)
                    {
                        FieldsDetails item = new FieldsDetails(DBf.Name, p.PropertyType, DBf.ItemType, p);
                        insColFieldsDetail.Add(item);
                    }
                }
            }
            #endregion

            StringBuilder QueryStr = new StringBuilder();
            QueryStr.Append("INSERT INTO ");// INSERT INTO
            QueryStr.Append(insTableName);//INSERT INTO DETALLEPEDIDO
            QueryStr.Append(" (");//INSERT INTO DETALLE PEDIDO (

            #region Agrega el Campo que corresponde con la tabla maestra del detalle
            if (masterField != "")
            {
                QueryStr.Append(masterField); //INSERT INTO DETALLEPEDIDO (idPedido
                QueryStr.Append(", ");// INSERT INTO DETALLEPEDIDO (idPedido, 
            }
            #endregion

            #region se agrega los campos en string de la consulta
            bool coma = false;
            foreach (Fields f in insColFields)
            {

                if (!f.isAutonumeric)
                {
                    if (coma)
                    {
                        QueryStr.Append(", ");
                    }
                    else
                    {
                        coma = true;
                    }
                    QueryStr.Append(f.Name);
                }
            }
            #endregion

            #region carga el string "?" en la consulta
            QueryStr.Append(") VALUES(");
            int cont = 0;
            coma = false;

            #region agrega el ? para el value de la tabla maestra del detalle
            if (masterFieldValue != null)
            {
                QueryStr.Append("?");
                cont++;
                coma = true;
            }
            #endregion

            foreach (Fields f in insColFields)
            {
                if (!f.isAutonumeric)
                {
                    if (coma)
                    {
                        QueryStr.Append(", ");
                    }
                    else
                    {
                        coma = true;
                    }
                    QueryStr.Append("?");
                    cont++;
                }
            }
            QueryStr.Append(")");
            #endregion

            #region carga los valores de los parametros en un array
            object[] colData = new object[cont];
            int lastIndexUsed = 0;

            #region agrega el valor del id de la tabla maestra en la coleccion de los parametros.
            if (masterFieldValue != null)
            {
                colData[lastIndexUsed] = masterFieldValue;
                lastIndexUsed++;
            }
            #endregion

            foreach (Fields f in insColFields)
            {
                if (!f.isAutonumeric)
                {
                    colData[lastIndexUsed] = getFieldValue(f, obj); //f.PropInfo.GetValue(obj, null);
                    lastIndexUsed++;
                }
            }
            #endregion

            #region Realiza la inserccion en la base de datos
            sql.SqlString = QueryStr.ToString();
            int res = sql.ExecuteNonQuery(colData);
            #endregion


            //agregado by rod par cargar el identiy de los detalles.
            OleDbDataReader DR = null;
            try
            {
                QueryStr = new StringBuilder();
                foreach (PropertyInfo p in PI)
                {
                    Object[] colObj = p.GetCustomAttributes(false);
                    foreach (Object auxObj in colObj)
                    {
                        DBField DBf = auxObj as DBField;
                        if (DBf != null)
                        {
                            if (DBf.IsAutonumeric)
                            {
                                QueryStr.Append(" select max(");
                                QueryStr.Append(DBf.Name);
                                QueryStr.Append(") from ");
                                QueryStr.Append(insTableName);
                                sql.SqlString = QueryStr.ToString();
                                DR = sql.GetDataReader();
                                if (DR.Read())
                                {
                                    p.SetValue(obj, DR[0], null);
                                }
                                DR.Close();

                            }
                        }

                    }
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                if (DR != null)
                {
                    DR.Close();
                    DR.Dispose();
                }
            }

            //fin del agregado by rod.


            /*#region Realiza la inserccion de los detalles si existen
            foreach (FieldsDetails fd in insColFieldsDetail)
            {
                DetailInsert(fd.PropInfo.GetValue(obj, null), fd.ItemType, sql);
            }
            #endregion*/

            //return insertRes;
        }

        /// <summary>
        /// Actualiza en la base de datos, recibe es objeto a acualizar.
        /// Si tiene detalles tambien los actualiza
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public bool Update(T obj)
        {
            string MasterField = "";
            object MasterFieldValue = null;
            StringBuilder QueryStr = new StringBuilder();
            QueryStr.Append("UPDATE ");
            QueryStr.Append(TableName);
            QueryStr.Append(" SET ");

            #region Se agrega campos en el string de consulta
            bool coma = false;
            int cont = 0;

            foreach (Fields f in colFields)
            {
                if (!f.isKey)
                {
                    if (coma)
                    {
                        QueryStr.Append(", ");
                    }
                    else
                    {
                        coma = true;
                    }
                    QueryStr.Append(f.Name);
                    QueryStr.Append(" = ");
                    QueryStr.Append("?");
                    cont++;
                }
            }
            #endregion

            #region Genera la condicion para el UPDATE
            int contCond = 0;
            QueryStr.Append(" WHERE ");
            bool bAND = false;

            foreach (Fields f in colFields)
            {
                if (f.isKey)
                {
                    if (bAND)
                    {
                        QueryStr.Append(" AND ");
                    }
                    else
                    {
                        bAND = true;
                    }
                    QueryStr.Append(f.Name);
                    QueryStr.Append(" = ");
                    QueryStr.Append("?");
                    contCond++;
                }
            }
            #endregion

            #region Carga los valores de los parametros en un array
            object[] colData = new object[cont + contCond];
            int lastIndexUsed = 0;
            foreach (Fields f in colFields)
            {
                if (!f.isKey)
                {
                    colData[lastIndexUsed] = getFieldValue(f, obj);
                    lastIndexUsed++;
                }
            }
            #endregion

            #region Carga los valores de los parametros en el array para la condicion
            foreach (Fields f in colFields)
            {
                if (f.isKey)
                {
                    colData[lastIndexUsed] = f.PropInfo.GetValue(obj, null);
                    MasterField = f.Name;
                    MasterFieldValue = f.PropInfo.GetValue(obj, null);
                    lastIndexUsed++;
                }
            }
            #endregion

            #region Realiza la consulta en la base de datos
            using (SQL sql = new SQL())
            {
                sql.SqlString = QueryStr.ToString();
                int res = 0;
                res = sql.ExecuteNonQuery(colData);
            #endregion

                #region Actualiza en lo detalles
                foreach (FieldsDetails fd in colFieldsDetail)
                {
                    DetailDelete(fd.PropInfo.GetValue(obj, null), fd.ItemType, sql, MasterField, MasterFieldValue);
                    //DetailUpdate(fd.PropInfo.GetValue(obj, null), fd.ItemType, sql, MasterField, MasterFieldValue);
                    DetailInsert(fd.PropInfo.GetValue(obj, null), fd.ItemType, sql, MasterField, MasterFieldValue);
                }
                #endregion
                return res > 0 ? true : false;
            }

        }

        /// <summary>
        /// Sin uso:
        /// Esta funcion se debe llamar desde el Update para actualizar cada detalle
        /// en la BD.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="itemType"></param>
        /// <param name="sql"></param>
        /// <param name="masterField"></param>
        /// <param name="masterFieldValue"></param>
        private void DetailUpdate(object obj, Type itemType, SQL sql, string masterField, object masterFieldValue)
        {
            String DetailTable = "";
            Object[] DBt = itemType.GetCustomAttributes(typeof(DBTable), true);//obtengo los atributos personalizados para el tipo de dato
            foreach (Object objA in DBt)//por cada atributo
            {
                DBTable t1 = objA as DBTable; //casteo a tipo DBTable
                if (t1 != null) //si no es null es porque el CustomAtributte es de tipo DBTable
                {
                    DetailTable = t1.ToString(); //lo guardo en la variable
                    break;//termino el bucle
                }
            }

            StringBuilder QueryString = new StringBuilder();
            QueryString.Append("DELETE FROM ");
            QueryString.Append(DetailTable);
            QueryString.Append(" WHERE ");
            QueryString.Append(masterField);
            QueryString.Append(" = ?");
        }

        /// <summary>
        /// Si uso:
        /// Se actualiza cada item del detalle. Se deberia llamar desde el Detail Update
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="t"></param>
        /// <param name="sql"></param>
        /// <param name="masterField"></param>
        /// <param name="masterFieldValue"></param>
        private void ItemDetailUpdate(object obj, Type t, SQL sql, string masterField, object masterFieldValue)
        {
            string updTableName = "";
            List<Fields> updColFields = new List<Fields>();
            List<FieldsDetails> updColFieldsDetail = new List<FieldsDetails>();

            #region Obtengo el updtableName
            Object[] DBt = t.GetCustomAttributes(typeof(DBTable), true);//obtengo los atributos personalizados para el tipo de dato T
            foreach (Object auxObj in DBt)//por cada atributo
            {
                DBTable t1 = auxObj as DBTable; //casteo a tipo DBTable
                if (t1 != null) //si no es null es porque el CustomAtributte es de tipo DBTable
                {
                    updTableName = t1.ToString(); //lo guardo en la variable
                    break;//termino el bucle
                }
            }
            #endregion

            #region Obtengo los campos
            PropertyInfo[] PI = t.GetProperties();
            foreach (PropertyInfo p in PI)
            {
                Object[] colObj = p.GetCustomAttributes(false);
                foreach (Object auxObj in colObj)
                {
                    DBField DBf = auxObj as DBField;
                    if (DBf != null)
                    {
                        Fields item = new Fields(DBf.ToString(), null, p.PropertyType, DBf.IsKey, DBf.IsAutonumeric, DBf.IsForeingKey, DBf.ForeingTable, p);
                        updColFields.Add(item);
                    }
                }
            }
            #endregion

            #region Obtengo los campos de detalle
            foreach (PropertyInfo p in PI)
            {
                Object[] colObj = p.GetCustomAttributes(false);
                foreach (Object auxObj in colObj)
                {
                    DBFieldDetail DBf = auxObj as DBFieldDetail;
                    if (DBf != null)
                    {
                        FieldsDetails item = new FieldsDetails(DBf.Name, p.PropertyType, DBf.ItemType, p);
                        updColFieldsDetail.Add(item);
                    }
                }
            }
            #endregion

            StringBuilder QueryStr = new StringBuilder();
            QueryStr.Append("UPDATE ");
            QueryStr.Append(updTableName);
            QueryStr.Append(" SET ");

            bool coma = false;
            int cont = 0;

            #region se agrega el campo correspondiente a la tabla maestra del detalle
            if (masterField != "")
            {
                QueryStr.Append(masterField);
                QueryStr.Append(" = ?");
                coma = true;
                cont++;
            }
            #endregion

            #region Se agrega campos en el string de consulta
            foreach (Fields f in updColFields)
            {
                if (coma)
                {
                    QueryStr.Append(", ");
                }
                else
                {
                    coma = true;
                }
                QueryStr.Append(f.Name);
                QueryStr.Append(" = ?");
                cont++;
            }
            #endregion

            #region Genera la condicion para el UPDATE
            QueryStr.Append(" WHERE ");
            bool bAND = false;

            if (masterField != "")
            {
                QueryStr.Append(masterField);
                QueryStr.Append(" = ?");
                cont++;
                bAND = true;
            }

            foreach (Fields f in updColFields)
            {
                if (f.isKey)
                {
                    if (bAND)
                    {
                        QueryStr.Append(" AND ");
                    }
                    else
                    {
                        bAND = true;
                    }
                    QueryStr.Append(f.Name);
                    QueryStr.Append(" = ?");
                    cont++;
                }
            }
            #endregion

            #region Carga los valores del parametro para el id de la clase maestra del detalle
            object[] colData = new object[cont];
            int lastIndexUsed = 0;
            if (masterFieldValue != null)
            {
                colData[lastIndexUsed] = masterFieldValue;
                lastIndexUsed++;
            }
            #endregion

            #region Carga los valores de los parametros en un array
            foreach (Fields f in updColFields)
            {
                colData[lastIndexUsed] = getFieldValue(f, obj);
                lastIndexUsed++;
            }
            #endregion

            #region Carga los valores de los parametros en el array para la condicion
            if (masterFieldValue != null)
            {
                colData[lastIndexUsed] = masterFieldValue;
                lastIndexUsed++;
            }

            foreach (Fields f in updColFields)
            {
                if (f.isKey)
                {
                    colData[lastIndexUsed] = getFieldValue(f, obj); // f.PropInfo.GetValue(obj, null);
                    lastIndexUsed++;
                }
            }
            #endregion

            #region Realiza la consulta en la base de datos
            sql.SqlString = QueryStr.ToString();
            int res = 0;
            res = sql.ExecuteNonQuery(colData);
            #endregion

            /*#region Actualiza en lo detalles
            foreach (FieldsDetails fd in updColFieldsDetail)
            {
                DetailUpdate(fd.PropInfo.GetValue(obj,null), fd.ItemType, sql, fd.Name, DR[fd.Name]);
            }
            #endregion*/
        }

        /// <summary>
        /// realiza el delete de un objeto, primero elimina el detalle.
        /// </summary>
        /// <param name="obj">objeto de entidad a eliminar</param>
        /// <returns>Devuelve la cantidad de filas eliminadas</returns>
        public int Delete(T obj)
        {
            string MasterField = "";
            object MasterFieldValue = null;
            StringBuilder QueryStr = new StringBuilder();
            QueryStr.Append("DELETE FROM ");
            QueryStr.Append(TableName);

            #region Genera la condicion para el DELETE
            QueryStr.Append(" WHERE ");
            bool bAND = false;
            int cont = 0;

            foreach (Fields f in colFields)
            {
                if (f.isKey)
                {
                    if (bAND)
                    {
                        QueryStr.Append(" AND ");
                    }
                    else
                    {
                        bAND = true;
                    }
                    QueryStr.Append(f.Name);
                    QueryStr.Append(" = ");
                    QueryStr.Append("?");
                    cont++;
                }
            }
            #endregion

            #region Carga los valores de los parametros en el array para la condicion
            object[] colData = new object[cont];
            int lastIndexUsed = 0;
            foreach (Fields f in colFields)
            {
                if (f.isKey)
                {
                    colData[lastIndexUsed] = f.PropInfo.GetValue(obj, null);
                    MasterField = f.Name;
                    MasterFieldValue = f.PropInfo.GetValue(obj, null);
                    lastIndexUsed++;
                }
            }
            #endregion

            #region Realiza la consulta en la base de datos
            using (SQL sql = new SQL())
            {

                #region  elimina los detalles
                foreach (FieldsDetails fd in colFieldsDetail)
                {
                    DetailDelete(fd.PropInfo.GetValue(obj, null), fd.ItemType, sql, MasterField, MasterFieldValue);
                }
                #endregion
                sql.SqlString = QueryStr.ToString();
                int res = 0;
                res = sql.ExecuteNonQuery(colData);
                return res;
            }

            #endregion
        }

        /// <summary>
        /// Elimina el detalle de un objeto
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="itemType"></param>
        /// <param name="sql"></param>
        /// <param name="masterField"></param>
        /// <param name="masterFieldValue"></param>
        private void DetailDelete(object obj, Type itemType, SQL sql, string masterField, object masterFieldValue)
        {
            string delTableName = "";

            #region Obtengo el delTableName
            Object[] DBt = itemType.GetCustomAttributes(typeof(DBTable), true);//obtengo los atributos personalizados para el tipo de dato T
            foreach (Object auxObj in DBt)//por cada atributo
            {
                DBTable t1 = auxObj as DBTable; //casteo a tipo DBTable
                if (t1 != null) //si no es null es porque el CustomAtributte es de tipo DBTable
                {
                    delTableName = t1.ToString(); //lo guardo en la variable
                    break;//termino el bucle
                }
            }
            #endregion

            StringBuilder QueryStr = new StringBuilder();
            QueryStr.Append("DELETE FROM ");
            QueryStr.Append(delTableName);

            #region Genera la condicion para el DELETE
            QueryStr.Append(" WHERE ");
            if (masterField != "")
            {
                QueryStr.Append(masterField);
                QueryStr.Append(" = ?");
            }
            #endregion

            #region Carga los valores de los parametros en el array para la condicion
            object[] colData = new object[1];
            int lastIndexUsed = 0;
            if (masterFieldValue != null)
            {
                colData[lastIndexUsed] = masterFieldValue;
                lastIndexUsed++;
            }
            #endregion

            #region Realiza la consulta en la base de datos
            sql.SqlString = QueryStr.ToString();
            int res = 0;
            res = sql.ExecuteNonQuery(colData);
            #endregion

            #region antigua forma, sin uso
            //ArrayList DetailsList = new ArrayList();
            //Type t = obj.GetType();
            //PropertyInfo[] PI = t.GetProperties();
            //foreach (PropertyInfo p in PI)
            //{
            //    object[] colObj = p.GetCustomAttributes(false);
            //    foreach (object o in colObj)
            //    {
            //        DBField DBf = o as DBField;
            //        if (DBf != null)
            //        {
            //            if (DBf.Name == "detail")
            //            {
            //                DetailsList = (ArrayList)p.GetValue(obj, null);
            //                break;
            //            }
            //        }
            //    }
            //}

            //foreach (object i in DetailsList)
            //{
            //    ItemDetailDelete(i, itemType, sql, masterField, masterFieldValue);
            //}
            #endregion
        }

        /// <summary>
        /// Sin uso:
        /// Elimina un item del detalle
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="t"></param>
        /// <param name="sql"></param>
        /// <param name="masterField"></param>
        /// <param name="masterFieldValue"></param>
        private void ItemDetailDelete(object obj, Type t, SQL sql, string masterField, object masterFieldValue)
        {
            string delTableName = "";
            List<Fields> delColFields = new List<Fields>();
            List<FieldsDetails> delColFieldsDetail = new List<FieldsDetails>();

            #region Obtengo el delTableName
            Object[] DBt = t.GetCustomAttributes(typeof(DBTable), true);//obtengo los atributos personalizados para el tipo de dato T
            foreach (Object auxObj in DBt)//por cada atributo
            {
                DBTable t1 = auxObj as DBTable; //casteo a tipo DBTable
                if (t1 != null) //si no es null es porque el CustomAtributte es de tipo DBTable
                {
                    delTableName = t1.ToString(); //lo guardo en la variable
                    break;//termino el bucle
                }
            }
            #endregion

            #region Obtengo los campos
            PropertyInfo[] PI = t.GetProperties();
            foreach (PropertyInfo p in PI)
            {
                Object[] colObj = p.GetCustomAttributes(false);
                foreach (Object auxObj in colObj)
                {
                    DBField DBf = auxObj as DBField;
                    if (DBf != null)
                    {
                        Fields item = new Fields(DBf.ToString(), null, p.PropertyType, DBf.IsKey, DBf.IsAutonumeric, DBf.IsForeingKey, DBf.ForeingTable, p);
                        delColFields.Add(item);
                    }
                }
            }
            #endregion

            #region Obtengo los campos de detalle
            foreach (PropertyInfo p in PI)
            {
                Object[] colObj = p.GetCustomAttributes(false);
                foreach (Object auxObj in colObj)
                {
                    DBFieldDetail DBf = auxObj as DBFieldDetail;
                    if (DBf != null)
                    {
                        FieldsDetails item = new FieldsDetails(DBf.Name, p.PropertyType, DBf.ItemType, p);
                        delColFieldsDetail.Add(item);
                    }
                }
            }
            #endregion

            StringBuilder QueryStr = new StringBuilder();
            QueryStr.Append("DELETE FROM ");
            QueryStr.Append(delTableName);

            #region Genera la condicion para el DELETE
            int cont = 0;
            bool bAND = false;

            QueryStr.Append(" WHERE ");
            if (masterField != "")
            {
                QueryStr.Append(masterField);
                QueryStr.Append(" = ?");
                cont++;
                bAND = true;
            }

            foreach (Fields f in delColFields)
            {
                if (f.isKey)
                {
                    if (bAND)
                    {
                        QueryStr.Append(" AND ");
                    }
                    else
                    {
                        bAND = true;
                    }
                    QueryStr.Append(f.Name);
                    QueryStr.Append(" = ?");
                    cont++;
                }
            }
            #endregion

            #region Carga los valores de los parametros en el array para la condicion
            object[] colData = new object[cont];
            int lastIndexUsed = 0;
            if (masterFieldValue != null)
            {
                colData[lastIndexUsed] = masterFieldValue;
                lastIndexUsed++;
            }

            foreach (Fields f in delColFields)
            {
                if (f.isKey)
                {
                    colData[lastIndexUsed] = getFieldValue(f, obj); // f.PropInfo.GetValue(obj, null);
                    lastIndexUsed++;
                }
            }
            #endregion

            #region Realiza la consulta en la base de datos
            sql.SqlString = QueryStr.ToString();
            int res = 0;
            res = sql.ExecuteNonQuery(colData);
            #endregion
        }

        /// <summary>
        /// Devuelve el string correspondiente de acuerdo al operador 
        /// </summary>
        /// <param name="Operator"></param>
        /// <returns></returns>
        private string ToStringOperator(filterOperator Operator)
        {
            switch (Operator)
            {
                case filterOperator.Equals:
                    return " = ";
                case filterOperator.BiggerThan:
                    return " > ";
                case filterOperator.SmallerThan:
                    return " < ";
                case filterOperator.Like:
                    return " LIKE ";
                case filterOperator.In:
                    return " IN ";
                case filterOperator.Distinct:
                    return " <> ";
                case filterOperator.BiggerEqualsThan:
                    return " >= ";
                case filterOperator.SmallerEqualsThan:
                    return " <= ";
                default:
                    return " ";
            }
        }

        /// <summary>
        /// Devuelve el string correspondiente de acuerdo al operador 
        /// en el caso que el valor sea null
        /// </summary>
        /// <param name="Operator"></param>
        /// <returns></returns>
        private string ToStringOperatorNull(filterOperator Operator)
        {
            switch (Operator)
            {
                case filterOperator.Equals:
                    return " is ";
                case filterOperator.BiggerThan:
                    return " > ";
                case filterOperator.SmallerThan:
                    return " < ";
                case filterOperator.Like:
                    return " LIKE ";
                case filterOperator.In:
                    return " IN ";
                case filterOperator.Distinct:
                    return " IS NOT ";
                default:
                    return " ";
            }
        }

        /// <summary>
        /// Obtengo el valor de un campo de un determinado objeto.
        /// Trata de manera diferente dependiendo si es o no ForeingKey
        /// </summary>
        /// <param name="f"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        private object getFieldValue(Fields f, object obj)
        {
            if (!f.isForeingKey)
            {
                return f.PropInfo.GetValue(obj, null);
            }
            else
            {
                object aux = f.PropInfo.GetValue(obj, null);
                if (aux != null)
                {
                    Type t = aux.GetType();
                    PropertyInfo[] PI = t.GetProperties();
                    foreach (PropertyInfo p in PI)
                    {
                        Object[] colObj = p.GetCustomAttributes(false);
                        foreach (Object auxO in colObj)
                        {
                            DBField DBf = auxO as DBField;
                            if (DBf != null)
                            {
                                if (DBf.Name.ToUpper() == f.foreingTableField.ToUpper())
                                {
                                    return p.GetValue(aux, null);
                                }
                            }
                        }
                    }
                }
            }
            return null;
        }

        /// <summary>
        ///Funcion que brinda una interfaz para poder ingresar 
        ///condiciones aplicadas a las consultas con operadores etc.
        ///(e.: Nombre = "Lucas") Utilizar el NOMBRE DE PROPIEDAD !!!!!!!
        /// </summary>
        /// <param name="FieldName"></param>
        /// <param name="Operator"></param>
        /// <param name="value"></param>
        public void AddFilter(string FieldName, filterOperator Operator, object value)
        {

            bool encontro = false;
            //TODO: tener en cuenta cuando el fieldname es foring key y entonces viene
            // un objetc que habria que analizar cuales son sus claves primarias.
            foreach (Fields f in colFields)
            {
                if (f.PropInfo.Name.ToUpper() == FieldName.ToUpper())
                {
                    if (sqlFilter.strQuery.ToString() != "")
                    {
                        sqlFilter.strQuery.Append(" AND ");
                    }
                    sqlFilter.strQuery.Append(f.Name);
                    if (value == null || (value is DateTime && (DateTime)value == DateTime.MinValue))
                    {
                        sqlFilter.strQuery.Append(ToStringOperatorNull(Operator));
                        sqlFilter.strQuery.Append("NULL");
                    }
                    else
                    {
                        sqlFilter.strQuery.Append(ToStringOperator(Operator));
                        sqlFilter.strQuery.Append("?");
                        sqlFilter.colData.Add(value);
                    }
                    encontro = true;
                    break; //para que seguir iterando?
                }
            }
            if (!encontro)
            {
                throw new ArgumentException("El campo: " + FieldName + " no fue encontrado entre las propiedades del objeto a filtrar.");
            }
        }

        /// <summary>
        /// Funcion que nos sirve para saber si un campo es valido asi saber si debe o no ser puesto en la lista de orden
        /// </summary>
        /// <param name="FieldName"></param>
        /// <returns></returns>
        public Boolean isValidField(string FieldName)
        {
            bool encontro = false;
            foreach (Fields f in colFields)
            {
                if (f.PropInfo.Name.ToUpper() == FieldName.ToUpper())
                {
                    encontro = true;
                    break;
                }
            }
            return encontro;
        }

        /// <summary>
        ///Funcion que brinda una interfaz para poder ingresar 
        ///el orden aplicado a las consultas.
        ///(e.: Order by Nombre")
        /// </summary>
        /// <param name="FieldName"></param>
        /// <param name="Direction"></param>
        public void AddOrder(string FieldName, Order.orderDirection Direction)
        {
            bool encontro = false;
            foreach (Fields f in colFields)
            {
                if (f.PropInfo.Name.ToUpper() == FieldName.ToUpper())
                {
                    if (sqlOrder.strQuery.ToString() != "")
                    {
                        sqlOrder.strQuery.Append(", ");
                    }
                    else
                    {
                        sqlOrder.strQuery.Append("ORDER BY ");
                    }
                    sqlOrder.strQuery.Append(f.Name);
                    sqlOrder.strQuery.Append(" ");
                    sqlOrder.strQuery.Append(Direction.ToString());
                    encontro = true;
                    break;
                }
            }
            if (!encontro)
            {
                throw new ArgumentException("No se encontro el campo: " + FieldName + " como propiedad para agregar en el order by");
            }
        }

        /// <summary>
        /// Borra la lista de filtros, para la consulta.
        /// </summary>
        public void ClearFilters()
        {
            sqlFilter.colData.Clear();
            sqlFilter.strQuery = new StringBuilder();
        }

        public void ClearOrders()
        {
            sqlOrder.strQuery = new StringBuilder();
        }

        /// <summary>
        /// Add filter para ints
        /// </summary>
        /// <param name="FieldName"></param>
        /// <param name="values"></param>
        public void AddFilterIN(string FieldName, params int[] values)
        {
            object[] array = new object[values.Length];
            int cont = 0;
            foreach (int i in values)
            {
                array[cont] = i;
                cont++;
            }
            AddFilterIN(FieldName, array);
        }

        /// <summary>
        /// Add filter para doubles
        /// </summary>
        /// <param name="FieldName"></param>
        /// <param name="values"></param>
        public void AddFilterIN(string FieldName, params double[] values)
        {
            object[] array = new object[values.Length];
            int cont = 0;
            foreach (double i in values)
            {
                array[cont] = i;
                cont++;
            }
            AddFilterIN(FieldName, array);
        }

        /// <summary>
        /// Add filter para decimals
        /// </summary>
        /// <param name="FieldName"></param>
        /// <param name="values"></param>
        public void AddFilterIN(string FieldName, params decimal[] values)
        {
            object[] array = new object[values.Length];
            int cont = 0;
            foreach (decimal i in values)
            {
                array[cont] = i;
                cont++;
            }
            AddFilterIN(FieldName, array);
        }

        /// <summary>
        /// Add filter para long
        /// </summary>
        /// <param name="FieldName"></param>
        /// <param name="values"></param>
        public void AddFilterIN(string FieldName, params long[] values)
        {
            object[] array = new object[values.Length];
            int cont = 0;
            foreach (long i in values)
            {
                array[cont] = i;
                cont++;
            }
            AddFilterIN(FieldName, array);
        }


        /// <summary>
        ///Funcion que brinda una interfaz para poder ingresar 
        ///condiciones aplicadas a las consultas con operador IN.
        ///(e.: Nombre IN ("Lucas")
        /// </summary>
        /// <param name="FieldName"></param>
        /// <param name="Operator"></param>
        /// <param name="value"></param>
        public void AddFilterIN(string FieldName, params object[] values)
        {
            #region Carga tabla y operador
            bool encontro = false;
            foreach (Fields f in colFields)
            {
                if (f.PropInfo.Name.ToUpper() == FieldName.ToUpper())
                {
                    if (sqlFilter.strQuery.ToString() != "")
                    {
                        sqlFilter.strQuery.Append(" AND ");
                    }
                    sqlFilter.strQuery.Append(f.Name); // CLIENTE
                    sqlFilter.strQuery.Append(" IN "); //CLIENTE IN
                    sqlFilter.strQuery.Append(" ("); //CLIENTE IN (


                    #region Carga los "?"
                    bool coma = false;
                    foreach (object value in values)//CLIENTE IN (?, ?, ?
                    {
                        if (coma)
                        {
                            sqlFilter.strQuery.Append(", ");
                        }
                        else
                        {
                            coma = true;
                        }
                        sqlFilter.strQuery.Append("?");
                    }
                    sqlFilter.strQuery.Append(")"); //CLIENTE IN (?, ?, ?)
                    #endregion

                    #region Carga los valores en la coleccion de valores del sqlFilter
                    foreach (object value in values)
                    {
                        sqlFilter.colData.Add(value);
                    }
                    #endregion
                    encontro = true;
                    break;
                }
            }
            if (!encontro)
            {
                throw new BussinessLogicTecnoPezException("El campo in: " + FieldName + " no existe. Verifique el codigo que llama al gestor generico.");
            }
            #endregion


        }


        //**********************Clases privadas***************************
        class Fields
        {
            public string Name;
            public Object Value;
            public Type fType;
            public bool isKey = false;
            public bool isAutonumeric = false;
            public bool isForeingKey = false;
            public string foreingTableField;
            public PropertyInfo PropInfo;


            /// <summary>
            /// Constructor
            /// </summary>
            /// <param name="Name"></param>
            /// <param name="value"></param>
            /// <param name="fType"></param>
            /// <param name="isKey"></param>
            /// <param name="isAutonumeric"></param>
            /// <param name="isForeingKey"></param>
            /// <param name="foreingTable"></param>
            /// <param name="Propinfo"></param>
            public Fields(string Name, Object value, Type fType, bool isKey, bool isAutonumeric, bool isForeingKey, string foreingTableField, PropertyInfo Propinfo)
            {
                this.Name = Name;
                this.Value = value;
                this.fType = fType;
                this.isKey = isKey;
                this.isAutonumeric = isAutonumeric;
                this.isForeingKey = isForeingKey;
                this.foreingTableField = foreingTableField;
                this.PropInfo = Propinfo;
            }
        }

        class FieldsDetails
        {
            public string Name;
            public Type fType;
            public Type ItemType;
            public PropertyInfo PropInfo;

            public FieldsDetails(string Name, Type fType, Type ItemType, PropertyInfo propInfo)
            {
                this.Name = Name;
                this.fType = fType;
                this.ItemType = ItemType;
                this.PropInfo = propInfo;
            }
        }

        /// <summary>
        ///Tipo de dato que sirve para hacer la condicion que va despues
        ///del WHERE en las consulas (ej.: IDCliente = 23).
        ///La interfaz para usar estos es el metodo AddFilter.
        /// </summary>
        public class Filter
        {
            public StringBuilder strQuery = new StringBuilder();
            public List<object> colData = new List<object>();
        }

        public class Order
        {
            public enum orderDirection
            {
                ASC,
                DESC,
            }
            public StringBuilder strQuery = new StringBuilder();
        }
        //****************************************************************
    }
}
